#ifndef __DLDXDG_SPDLOG_H__
#define __DLDXDG_SPDLOG_H__

#include <iostream>
#include <sstream>
#include <ctime>
#include <iomanip>   // std::put_time 在这里定义

#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include "spdlog/sinks/stdout_color_sinks.h" // 控制台 sink
#include "spdlog/sinks/rotating_file_sink.h"  // 滚动文件 sink

// --- API 声明区 ---
void xdg_spdlog_init();
void xdg_spdlog_shutdown();

// 宏定义 (替换旧的 write_log 函数)
// __VA_ARGS__ 允许我们传递格式化参数，如 LOG_INFO("User {}", "Alice");
#define LOG_ALL_TRACE(...) SPDLOG_LOGGER_CALL(get_all_logs_logger_async(), spdlog::level::trace, __VA_ARGS__)
#define LOG_ALL_DEBUG(...) SPDLOG_LOGGER_CALL(get_all_logs_logger_async(), spdlog::level::debug, __VA_ARGS__)
#define LOG_ALL_INFO(...)  SPDLOG_LOGGER_CALL(get_all_logs_logger_async(), spdlog::level::info,  __VA_ARGS__)
#define LOG_ALL_WARN(...)  SPDLOG_LOGGER_CALL(get_all_logs_logger_async(), spdlog::level::warn,  __VA_ARGS__)
#define LOG_ALL_ERROR(...) SPDLOG_LOGGER_CALL(get_all_logs_logger_async(), spdlog::level::err,   __VA_ARGS__)
#define LOG_ALL_CRITICAL(...) SPDLOG_LOGGER_CALL(get_all_logs_logger_async(), spdlog::level::critical, __VA_ARGS__)

// 存储数据用的日志
#define LOG_DATA_INFO(...)     SPDLOG_LOGGER_CALL(get_data_logs_logger_async(), spdlog::level::info, __VA_ARGS__)
#define LOG_DATA_WARN(...)     SPDLOG_LOGGER_CALL(get_data_logs_logger_async(), spdlog::level::warn,     __VA_ARGS__)
#define LOG_DATA_ERROR(...)    SPDLOG_LOGGER_CALL(get_data_logs_logger_async(), spdlog::level::err,      __VA_ARGS__)
#define LOG_DATA_CRITICAL(...) SPDLOG_LOGGER_CALL(get_data_logs_logger_async(), spdlog::level::critical, __VA_ARGS__)

// 专门用于控制台的宏，通过名字获取 logger
//#define CONSOLE_LOG_TRACE(...) SPDLOG_LOGGER_CALL(spdlog::get("my_async_console"), spdlog::level::trace, __VA_ARGS__)
//#define CONSOLE_LOG_DEBUG(...) SPDLOG_LOGGER_CALL(spdlog::get("my_async_console"), spdlog::level::debug, __VA_ARGS__)
#define CONSOLE_LOG_INFO(...)  SPDLOG_LOGGER_CALL(spdlog::get("my_async_console"), spdlog::level::info,  __VA_ARGS__)
#define CONSOLE_LOG_WARN(...)  SPDLOG_LOGGER_CALL(spdlog::get("my_async_console"), spdlog::level::warn,  __VA_ARGS__)
#define CONSOLE_LOG_ERROR(...) SPDLOG_LOGGER_CALL(spdlog::get("my_async_console"), spdlog::level::err,   __VA_ARGS__)
#define CONSOLE_LOG_CRITICAL(...) SPDLOG_LOGGER_CALL(spdlog::get("my_async_console"), spdlog::level::critical, __VA_ARGS__)

// ========== 组合宏：同时写入所有日志和控制台 ==========
#define LOG_CONSOLE_TRACE(...) \
    do { \
        LOG_ALL_TRACE(__VA_ARGS__); \
        CONSOLE_LOG_TRACE(__VA_ARGS__); \
    } while(0)

#define LOG_CONSOLE_DEBUG(...) \
    do { \
        LOG_ALL_DEBUG(__VA_ARGS__); \
        CONSOLE_LOG_DEBUG(__VA_ARGS__); \
    } while(0)

#define LOG_CONSOLE_INFO(...) \
    do { \
        LOG_ALL_INFO(__VA_ARGS__); \
        CONSOLE_LOG_INFO(__VA_ARGS__); \
    } while(0)

#define LOG_CONSOLE_WARN(...) \
    do { \
        LOG_ALL_WARN(__VA_ARGS__); \
        CONSOLE_LOG_WARN(__VA_ARGS__); \
    } while(0)

#define LOG_CONSOLE_ERROR(...) \
    do { \
        LOG_ALL_ERROR(__VA_ARGS__); \
        CONSOLE_LOG_ERROR(__VA_ARGS__); \
    } while(0)

#define LOG_CONSOLE_CRITICAL(...) \
    do { \
        LOG_ALL_CRITICAL(__VA_ARGS__); \
        CONSOLE_LOG_CRITICAL(__VA_ARGS__); \
    } while(0)



// 当天日期格式化函数 2025-09-10
inline std::string get_date_str(int days_offset = 0) {
    auto now = std::chrono::system_clock::now();

    // 根据偏移量调整时间
    now += std::chrono::hours(24) * days_offset;

    auto in_time_t = std::chrono::system_clock::to_time_t(now);

    std::tm tm_buf{};

#ifdef _WIN32
    // Windows 使用 localtime_s
    if (localtime_s(&tm_buf, &in_time_t) != 0) {
        return "1970-01-01"; // 错误处理
    }
#else
    // Linux/macOS 使用 localtime_r
    if (localtime_r(&in_time_t, &tm_buf) == nullptr) {
        return "1970-01-01"; // 错误处理
    }
#endif

    std::ostringstream ss;
    ss << std::put_time(&tm_buf, "%Y-%m-%d");
    return ss.str();
}

// 1: 全量日志
inline std::shared_ptr<spdlog::logger> get_all_logs_logger_async() {
    // 步骤 1: 生成今天 logger 的唯一名称，例如 "all_logs_2025-09-13"
    std::string today_str = get_date_str(0);
    std::string logger_name = "all_logs_" + today_str;

    // 步骤 2: 尝试从 spdlog 全局注册表中获取该 logger
    std::shared_ptr<spdlog::logger> logger = spdlog::get(logger_name);

    // 步骤 3: 如果 logger 不存在 (返回 nullptr)，说明这是今天第一次调用
    // 我们需要创建、配置并注册一个新的 logger 实例
    if (logger == nullptr) {
        // 步骤 3.1 (可选但推荐): 清理昨天的 logger 以释放资源
        // 构造昨天的 logger 名称并从注册表中移除它
        std::string yesterday_logger_name = "all_logs_" + get_date_str(-1);
        spdlog::drop(yesterday_logger_name);

        // 步骤 3.2: 创建今天的日志文件名，例如 "Logs/all_logs-2025-09-13.log"
        std::string filename = "Logs/all/all_logs-" + today_str + ".log";

        // 步骤 3.3: 创建一个“按大小滚动”的文件 sink
        // 参数: 文件名, 单个文件最大体积 (10MB), 最多保留文件数 (5个)
        std::shared_ptr<spdlog::sinks::rotating_file_sink_mt> sink =
            std::make_shared<spdlog::sinks::rotating_file_sink_mt>(filename, 1024 * 1024 * 10, 5);

        // 步骤 3.4: 获取 spdlog 全局的异步线程池指针
        std::shared_ptr<spdlog::details::thread_pool> tp = spdlog::thread_pool();

        // 步骤 3.5: 创建一个异步 logger 实例
        // 参数: logger 名称, sink 对象, 线程池指针, 队列溢出策略 (阻塞)
        logger = std::make_shared<spdlog::async_logger>(logger_name, sink, tp, spdlog::async_overflow_policy::block);

        // 步骤 3.6: 配置 logger
        logger->set_level(spdlog::level::trace); // logger 本身的级别设为最低，不过滤任何消息
        logger->flush_on(spdlog::level::info);   // 当记录 warn 或更高级别的日志时，自动刷新缓冲区
        logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%s:%#] %v"); // 设置日志格式

        // 步骤 3.7: 将新创建的 logger 注册到全局注册表
        spdlog::register_logger(logger);
    }

    // 步骤 4: 返回 logger 指针 (无论是刚创建的还是之前获取到的)
    return logger;
}

// 2: 数据日志
inline std::shared_ptr<spdlog::logger> get_data_logs_logger_async() {
    // (此函数逻辑与上面完全相同，只是名字和参数不同)
    std::string today_str = get_date_str(0);
    std::string logger_name = "data_logs_" + today_str;
    std::shared_ptr<spdlog::logger> logger = spdlog::get(logger_name);

    if (logger == nullptr) {
        spdlog::drop("data_logs_" + get_date_str(-1));
        std::string filename = "Logs/data/data_logs-" + today_str + ".log";
        // 数据日志文件
        std::shared_ptr<spdlog::sinks::rotating_file_sink_mt> sink =
            std::make_shared<spdlog::sinks::rotating_file_sink_mt>(filename, 1024 * 1024 * 10, 5);

        std::shared_ptr<spdlog::details::thread_pool> tp = spdlog::thread_pool();
        logger = std::make_shared<spdlog::async_logger>(logger_name, sink, tp, spdlog::async_overflow_policy::block);

        logger->set_level(spdlog::level::info); // 关键区别: 此 logger 只处理 info 及以上的日志
        logger->flush_on(spdlog::level::warn);
        logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");
        spdlog::register_logger(logger);
    }
    return logger;
}


// 初始化spdlog日志
inline void xdg_spdlog_init() {
    // 定义一个静态的 once_flag，用于保证初始化代码只执行一次
    static std::once_flag init_flag;
    try {
        std::call_once(init_flag, []() {
            // 1. 初始化 spdlog 的全局线程池，这是所有异步 logger 的动力源泉
            spdlog::init_thread_pool(8192, 1);

            // 不再使用会抛出异常的快捷函数，而是采用更安全的“检查-创建-注册”模式
            // 2. 初始化控制台 logger
            auto console_logger_name = "my_async_console";
            if (spdlog::get(console_logger_name) == nullptr) { // 检查是否存在
                auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
                console_sink->set_level(spdlog::level::trace); // 在 sink 层面设置最低级别

                auto tp = spdlog::thread_pool();
                auto logger = std::make_shared<spdlog::async_logger>(console_logger_name, console_sink, tp, spdlog::async_overflow_policy::block);

                logger->set_level(spdlog::level::info); // logger 层面也设为最低，方便后续通过宏来过滤
                logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v"); // 控制台日志通常不需要位置信息

                spdlog::register_logger(logger);
            }

            });
    }
    catch (const spdlog::spdlog_ex& ex) {
        std::cout << "Log 初始化失败: " << ex.what() << std::endl;
    }
}



// 销毁日志系统
inline void xdg_spdlog_shutdown() {
    // 调用 spdlog 的全局 shutdown 函数
    // 它会安全地关闭线程池，并刷新所有队列中的日志
    spdlog::shutdown();
}


#endif // __DLDXDG_SPDLOG_H__